package city.spring.configure.security.handler;

import city.spring.modules.system.service.impl.ClientDetailsServiceImpl;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.security.core.Authentication;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.provider.*;
import org.springframework.security.oauth2.provider.endpoint.TokenEndpoint;
import org.springframework.security.oauth2.provider.password.ResourceOwnerPasswordTokenGranter;
import org.springframework.security.oauth2.provider.token.AbstractTokenGranter;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.stereotype.Component;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Collections;

/**
 * 自定义登录成功处理程序
 *
 * @author HouKunLin
 * @date 2019/12/7 0007 23:16
 */
@Component
public class CustomLoginSuccessHandler implements AuthenticationSuccessHandler {
    private final static Logger logger = LoggerFactory.getLogger(CustomLoginSuccessHandler.class);
    private final boolean isDebug = logger.isDebugEnabled();
    /**
     * 用来构建json数据
     */
    private final ObjectMapper objectMapper;
    /**
     * 默认的TokenService，用于生成Token信息（会自动保存和更新Token信息）
     */
    private final DefaultTokenServices tokenServices;
    /**
     * 用来创建 TokenRequest 和 OAuth2Request 对象
     */
    private final OAuth2RequestFactory requestFactory;
    /**
     * 要与OAuth2共存，需要提供一个默认的Client
     */
    private final ClientDetails clientDetails;

    public CustomLoginSuccessHandler(
            ObjectMapper objectMapper,
            DefaultTokenServices defaultTokenServices,
            ClientDetailsServiceImpl clientDetailsService,
            OAuth2RequestFactory requestFactory) {
        this.objectMapper = objectMapper;
        this.tokenServices = defaultTokenServices;
        this.requestFactory = requestFactory;
        this.clientDetails = clientDetailsService.loadClientByClientId("password");
    }

    /**
     * 登录成功信息处理
     *
     * @param request        请求
     * @param response       响应
     * @param authentication 身份验证
     * @throws IOException      异常
     * @throws ServletException 异常
     * @see TokenEndpoint#postAccessToken(java.security.Principal, java.util.Map) 参照获取 TokenRequest 和 OAuth2AccessToken
     * @see ResourceOwnerPasswordTokenGranter#getOAuth2Authentication(ClientDetails, TokenRequest) 参照获取OAuth2Request和OAuth2Authentication
     * @see AbstractTokenGranter#grant(String, TokenRequest) 参照获取OAuth2AccessToken
     * @see AbstractTokenGranter#getAccessToken(ClientDetails, TokenRequest) 参照获取OAuth2AccessToken
     */
    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
        logger.info("登录成功:{}", authentication);
        // see L98 org.springframework.security.oauth2.provider.endpoint.TokenEndpoint
        TokenRequest tokenRequest = requestFactory.createTokenRequest(Collections.emptyMap(), clientDetails);
        // see L85 org.springframework.security.oauth2.provider.password.ResourceOwnerPasswordTokenGranter.ResourceOwnerPasswordTokenGranter
        OAuth2Request oAuth2Request = requestFactory.createOAuth2Request(clientDetails, tokenRequest);
        // see L86 org.springframework.security.oauth2.provider.password.ResourceOwnerPasswordTokenGranter.ResourceOwnerPasswordTokenGranter
        OAuth2Authentication oAuth2Authentication = new OAuth2Authentication(oAuth2Request, authentication);
        // see L132 org.springframework.security.oauth2.provider.endpoint.TokenEndpoint
        // see L67 org.springframework.security.oauth2.provider.token.AbstractTokenGranter
        OAuth2AccessToken accessToken = tokenServices.createAccessToken(oAuth2Authentication);
        response.setStatus(HttpStatus.OK.value());
        response.setContentType("application/json; charset=UTF-8");
        response.getWriter().write(objectMapper.writeValueAsString(accessToken));
    }
}
